using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.IO;
using System.Collections;
using System.Reflection;

namespace LinqToWmi.ProtoGenerator
{
    /// <summary>
    /// General WMI file generator
    /// </summary>
    public class WmiFileGenerator
    {
        private string _namespace;
        private string _outputLanguage;

        /// <summary>
        /// Sets the output namespace
        /// </summary>
        public string Namespace
        {
            get
            {
                return _namespace;
            }
            set
            {
                _namespace = value;
            }
        }

        /// <summary>
        /// Sets the output language
        /// </summary>
        public string OutputLanguage
        {
            get
            {
                return _outputLanguage;
            }
            set
            {
                _outputLanguage = value;
            }
        }

        /// <summary>
        /// Wraps an propertydatacollection such that its usable with D
        /// </summary>
        private IEnumerable<PropertyData> Wrap(PropertyDataCollection collection)
        {
            foreach (PropertyData c in collection)
            {
                yield return c;
            }
        }

        /// <summary>
        /// Create an fieldname
        /// </summary>
        private string GetFieldName(string name)
        {
            return String.Format("_{0}{1}", name.Substring(0, 1).ToLower(), name.Substring(1));
        }

        /// <summary>
        /// Create a property
        /// </summary>
        private CodeMemberProperty CreateProperty(PropertyData data)
        {
            var p = new CodeMemberProperty()
            {
                Name = data.Name
            };

            p.Attributes = MemberAttributes.Public;

            p.SetStatements.Add(new CodeAssignStatement(
                new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), GetFieldName(data.Name)),
                new CodeArgumentReferenceExpression("value")));

            p.GetStatements.Add(new CodeMethodReturnStatement(
                new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), GetFieldName(data.Name))));

            p.Type = new CodeTypeReference(CimTypeConverter.Convert(data));
            p.Comments.Add(new CodeCommentStatement(String.Format("<summary>\n Represents the property {0}\n </summary>", data.Name), true));
            return p;
        }

        private CodeAttributeArgument CreateAttributeArgument(string paramValue)
        {
            return new CodeAttributeArgument(String.Empty, new CodePrimitiveExpression(paramValue));
        }

        /// <summary>
        /// Generate the WMI output file
        /// </summary>
        public void GenerateFile(ManagementObject mgo, string name, string outfile)
        {

            var imports = new string[] { "System", "System.Linq", "LinqToWmi.Core.WMI" };

            var generatedNamespace = new CodeNamespace(_namespace);

            Assembly generatorAssembly = GetType().Assembly;
            string versionNumber = generatorAssembly.GetName().Version.ToString();
            generatedNamespace.Comments.Add(new CodeCommentStatement(String.Format("<auto-generated>\n This code was generated by a tool.\n LinqToWmi.ClassGenerator Version: {0}\n\n Changes to this file may cause incorrect behavior and will be lost if  the code is regenerated.\n </auto-generated>", versionNumber)));

            var generatedClass = new CodeTypeDeclaration(name);

            generatedClass.CustomAttributes.Add(new CodeAttributeDeclaration("System.CodeDom.Compiler.GeneratedCodeAttribute",
                                     CreateAttributeArgument(generatorAssembly.GetName().Name),
                                     CreateAttributeArgument(versionNumber)
                                     ));

            generatedNamespace.Imports.AddRange((from import in imports
                                                 select new CodeNamespaceImport(import)).ToArray());

            IEnumerable<CodeMemberField> wmiProperties = from property in Wrap(mgo.Properties)
                                                         select new CodeMemberField(
                                                                    new CodeTypeReference(CimTypeConverter.Convert(property)),
                                                                    GetFieldName(property.Name)
                                                                );

            generatedClass.Members.AddRange(wmiProperties.ToArray());

            generatedClass.Members.AddRange((from property in Wrap(mgo.Properties)
                                             select CreateProperty(property)).ToArray());

            generatedNamespace.Types.Add(generatedClass);

            //Write the file using the given code provider
            CodeDomProvider provider = CodeDomProvider.CreateProvider(_outputLanguage);

            string output = (outfile != null ? outfile :
                String.Format("{0}.{1}", name, provider.FileExtension));

            using (TextWriter writer = new StreamWriter(File.OpenWrite(output)))
            {
                provider.GenerateCodeFromNamespace(generatedNamespace, writer, null);
            }
        }
    }
}
